#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "MSTK.h"


#ifdef __cplusplus
extern "C" {
#endif

  /* 
     This function receive mesh from processor rank in communicator comm
     attrib list is received, but no attrib values.
     call MESH_RecvAttr()
     send_rank: the rank of sending processor
     rank: the rank of receiving processor
  */

int MESH_Vol_RecvMesh(Mesh_ptr mesh, int send_rank, int rank, MPI_Comm comm);
int MESH_Surf_RecvMesh(Mesh_ptr mesh, int send_rank, int rank, MPI_Comm comm);


int MESH_RecvMesh(Mesh_ptr mesh, int dim, int send_rank, int rank, MPI_Comm comm) {

  if (dim == 3)
    MESH_Vol_RecvMesh(mesh,send_rank,rank,comm);
  else if(dim == 2) 
    MESH_Surf_RecvMesh(mesh,send_rank,rank,comm);
  else {
    MSTK_Report("MESH_RecvMesh()","only receive volume or surface mesh",ERROR);
    exit(-1);
  }
  return 1;
}
  

int MESH_Surf_RecvMesh(Mesh_ptr mesh, int send_rank, int rank, MPI_Comm comm) {
  int i, j, nfv, nfvs, nfvs_local, natt, count, ncomp;
  int nv, nf, mesh_info[5];
  MVertex_ptr *verts, *fverts;
  MFace_ptr mf;
  MPI_Status status;
  RepType rtype;
  char attname[256];
  MType mtype;
  MAttType att_type;
  MAttrib_ptr attrib;
  int *list_attr;
  char *list_attr_names;
  double coor[3];
  /* mesh_info store the mesh reptype, nv, nf, nfvs and natt */
  /* receive mesh_info */
  MPI_Recv(mesh_info,5,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);

  /* basic mesh information */
  rtype = mesh_info[0];
  nv = mesh_info[1];
  nf = mesh_info[2];
  nfvs = mesh_info[3];
  natt = mesh_info[4];
  
  MESH_SetRepType(mesh,rtype);

  /* allocate receive buffer */
  int *list_vertex = (int *)MSTK_malloc(3*nv*sizeof(int));
  double *list_coor = (double *)MSTK_malloc(3*nv*sizeof(double));
  int *list_face = (int *)MSTK_malloc(nfvs*sizeof(int));
  /* receive vertex */
  MPI_Recv(list_vertex,3*nv,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);
  /* printf("num of vertex received %d on rank %d\n",nv,rank); */
  MPI_Recv(list_coor,3*nv,MPI_DOUBLE,send_rank,rank,comm,&status);
  verts = (MVertex_ptr *) malloc(nv*sizeof(MVertex_ptr));
  for(i = 0; i < nv; i++) {
    verts[i] = MV_New(mesh);
    MV_Set_GEntDim(verts[i],(list_vertex[3*i] & 7));
    MV_Set_GEntID(verts[i],(list_vertex[3*i] >> 3));
    MV_Set_PType(verts[i],(list_vertex[3*i+1] & 3));
    MV_Set_MasterParID(verts[i],(list_vertex[3*i+1] >> 2));
    MV_Set_GlobalID(verts[i],list_vertex[3*i+2]);
    coor[0] = list_coor[i*3];
    coor[1] = list_coor[i*3+1];
    coor[2] = list_coor[i*3+2];
    MV_Set_Coords(verts[i],coor);
  }

  /* receive face */

  MPI_Recv(list_face,nfvs,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);
  /*  printf("num of face received %d on rank %d\n", nf, rank); */
  fverts = (MVertex_ptr *) malloc(MAXPV2*sizeof(MVertex_ptr));
  nfvs_local = 0;
  for (i = 0; i < nf; i++) {
    mf = MF_New(mesh);
    nfv = list_face[nfvs_local];
    for (j = 0; j < nfv; j++) {
      fverts[j] = verts[list_face[nfvs_local+j+1]-1];
    }
    MF_Set_GEntDim(mf,(list_face[nfvs_local+nfv+1] & 7));
    MF_Set_GEntID(mf,(list_face[nfvs_local+nfv+1] >> 3));
    MF_Set_PType(mf,(list_face[nfvs_local+nfv+2] & 3));
    MF_Set_MasterParID(mf,(list_face[nfvs_local+nfv+2] >> 2));
    MF_Set_GlobalID(mf,list_face[nfvs_local+nfv+3]);

    MF_Set_Vertices(mf,nfv,fverts);
    nfvs_local += (nfv + 4);
  }
  if (fverts) free(fverts);
  if (verts) free(verts);


  /* receive attr */
  if(natt) {
    list_attr = (int *)MSTK_malloc(natt*sizeof(int));
    list_attr_names = (char *)MSTK_malloc((natt)*256);
    MPI_Recv(list_attr,natt,MPI_INT,send_rank,rank,comm,&status);
    MPI_Recv(list_attr_names,natt*256,MPI_CHAR,send_rank,rank,comm,&status);
    MPI_Get_count(&status,MPI_INT,&count);
    for(i = 0; i < natt; i++) {
      strcpy(attname,&list_attr_names[i*256]);
      /* see of the attrib exists */
      if(MESH_AttribByName(mesh,attname))
	continue;
      att_type = list_attr[i] & 7;
      mtype = (list_attr[i] >> 3) & 7;
      ncomp = list_attr[i] >> 6;
      if(ncomp == 1)
	attrib =  MAttrib_New(mesh, attname, att_type, mtype);
      else
	attrib =  MAttrib_New(mesh, attname, att_type, mtype, ncomp);
      printf("attr %d with name %s received on rank %d\n",i, attname, rank);
    }
    MSTK_free(list_attr);    
    MSTK_free(list_attr_names);
  }
    
  MSTK_free(list_vertex);    
  MSTK_free(list_face);    
  MSTK_free(list_coor);
  return 1;
}

int MESH_Vol_RecvMesh(Mesh_ptr mesh, int send_rank, int rank, MPI_Comm comm) {
  int i, j, nrv, nrvs, nrvs_local, count, natt, ncomp;
  int nv, nr, mesh_info[5];
  MVertex_ptr *verts, *rverts;
  MRegion_ptr mr;
  MPI_Status status;
  RepType rtype;
  char attname[256];
  MType mtype;
  MAttType att_type;
  MAttrib_ptr attrib;
  int *list_attr;
  char *list_attr_names;
  double coor[3];
  /* mesh_info store the mesh reptype, nv, nr, nrvs and natt */
  MPI_Recv(mesh_info,5,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);

  /* basic mesh information */
  rtype = mesh_info[0];
  nv = mesh_info[1];
  nr = mesh_info[2];
  nrvs = mesh_info[3];
  natt = mesh_info[4];

  MESH_SetRepType(mesh,rtype);

  /* allocate receive buffer */
  int *list_vertex = (int *)MSTK_malloc(3*nv*sizeof(int));
  double *list_coor = (double *)MSTK_malloc(3*nv*sizeof(double));
  int *list_region = (int *)MSTK_malloc(nrvs*sizeof(int));

  /* receive vertex */
  MPI_Recv(list_vertex,3*nv,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);

  /* printf("num of vertex received %d on rank %d\n",nv,rank); */

  MPI_Recv(list_coor,3*nv,MPI_DOUBLE,send_rank,rank,comm,&status);
  verts = (MVertex_ptr *) malloc(nv*sizeof(MVertex_ptr));
  for(i = 0; i < nv; i++) {
    verts[i] = MV_New(mesh);
    MV_Set_GEntDim(verts[i],(list_vertex[3*i] & 7));
    MV_Set_GEntID(verts[i],(list_vertex[3*i] >> 3));
    MV_Set_PType(verts[i],(list_vertex[3*i+1] & 3));
    MV_Set_MasterParID(verts[i],(list_vertex[3*i+1] >> 2));
    MV_Set_GlobalID(verts[i],list_vertex[3*i+2]);
    coor[0] = list_coor[i*3];
    coor[1] = list_coor[i*3+1];
    coor[2] = list_coor[i*3+2];
    MV_Set_Coords(verts[i],coor);
  }

  /* receive region */

  MPI_Recv(list_region,nrvs,MPI_INT,send_rank,rank,comm,&status);
  MPI_Get_count(&status,MPI_INT,&count);
  /*  printf("num of region received %d on rank %d\n", nr, rank); */
  rverts = (MVertex_ptr *) malloc(MAXPV3*sizeof(MVertex_ptr));
  nrvs_local = 0;
  for (i = 0; i < nr; i++) {
    mr = MR_New(mesh);
    nrv = list_region[nrvs_local];
    for (j = 0; j < nrv; j++) {
      rverts[j] = verts[list_region[nrvs_local+j+1]-1];
    }
    MR_Set_GEntDim(mr,(list_region[nrvs_local+nrv+1] & 7));
    MR_Set_GEntID(mr,(list_region[nrvs_local+nrv+1] >> 3));
    MR_Set_PType(mr,(list_region[nrvs_local+nrv+2] & 3));
    MR_Set_MasterParID(mr,(list_region[nrvs_local+nrv+2] >> 2));
    MR_Set_GlobalID(mr,list_region[nrvs_local+nrv+3]);

    MR_Set_Vertices(mr,nrv,rverts,0,NULL);
    nrvs_local += (nrv + 4);
  }
  if (rverts) free(rverts);
  if (verts) free(verts);

  /* receive attr */
  if(natt) {
    list_attr = (int *)MSTK_malloc(natt*sizeof(int));
    list_attr_names = (char *)MSTK_malloc((natt)*256);
    
    MPI_Recv(list_attr,natt,MPI_INT,send_rank,rank,comm,&status);
    MPI_Recv(list_attr_names,natt*256,MPI_CHAR,send_rank,rank,comm,&status);
    MPI_Get_count(&status,MPI_INT,&count);

    for(i = 0; i < natt; i++) {
      strcpy(attname,&list_attr_names[i*256]);
      /* see of the attrib exists */
      if(MESH_AttribByName(mesh,attname))
	continue;
      att_type = list_attr[i] & 7;
      mtype = (list_attr[i] >> 3) & 7;
      ncomp = list_attr[i] >> 6;
      if(ncomp == 1)
	attrib =  MAttrib_New(mesh, attname, att_type, mtype);
      else
	attrib =  MAttrib_New(mesh, attname, att_type, mtype, ncomp);
      /* printf("attr %d with name %s received on rank %d\n",i, attname, rank); */
    }
    MSTK_free(list_attr);    
    MSTK_free(list_attr_names);
  }

  MSTK_free(list_vertex);    
  MSTK_free(list_region);    
  MSTK_free(list_coor);    
  return 1;
}

#ifdef __cplusplus
}
#endif

